home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / pico / display.c < prev    next >
C/C++ Source or Header  |  1996-07-10  |  54KB  |  2,384 lines

  1. #if    !defined(lint) && !defined(DOS)
  2. static char rcsid[] = "$Id: display.c,v 4.67 1996/07/10 16:58:32 mikes Exp $";
  3. #endif
  4. /*
  5.  * Program:    Display functions
  6.  *
  7.  *
  8.  * Michael Seibel
  9.  * Networks and Distributed Computing
  10.  * Computing and Communications
  11.  * University of Washington
  12.  * Administration Builiding, AG-44
  13.  * Seattle, Washington, 98195, USA
  14.  * Internet: mikes@cac.washington.edu
  15.  *
  16.  * Please address all bugs and comments to "pine-bugs@cac.washington.edu"
  17.  *
  18.  *
  19.  * Pine and Pico are registered trademarks of the University of Washington.
  20.  * No commercial use of these trademarks may be made without prior written
  21.  * permission of the University of Washington.
  22.  * 
  23.  * Pine, Pico, and Pilot software and its included text are Copyright
  24.  * 1989-1996 by the University of Washington.
  25.  * 
  26.  * The full text of our legal notices is contained in the file called
  27.  * CPYRIGHT, included with this distribution.
  28.  *
  29.  */
  30. /*
  31.  * The functions in this file handle redisplay. There are two halves, the
  32.  * ones that update the virtual display screen, and the ones that make the
  33.  * physical display screen the same as the virtual display screen. These
  34.  * functions use hints that are left in the windows by the commands.
  35.  *
  36.  */
  37.  
  38. #include        <stdio.h>
  39. #include    "osdep.h"
  40. #include        "pico.h"
  41. #include    "estruct.h"
  42. #include        "edef.h"
  43. #include        "efunc.h"
  44.  
  45.  
  46. #ifdef    ANSI
  47.     int vtmove(int, int);
  48.     int vtputc(CELL);
  49.     int vtpute(CELL);
  50.     int vteeol(void);
  51.     int updateline(int, CELL *, CELL *, short *);
  52.     int updext(void);
  53.     int mlputi(int, int);
  54.     int mlputli(long, int);
  55.     int showCompTitle(void);
  56.     int nlforw(void);
  57.     int dumbroot(int, int);
  58.     int dumblroot(long, int);
  59. #else
  60.     int vtmove();
  61.     int vtputc();
  62.     int vtpute();
  63.     int vteeol();
  64.     int updateline();
  65.     int updext();
  66.     int mlputi();
  67.     int mlputli();
  68.     int showCompTitle();
  69.     int nlforw();
  70.     int dumbroot();
  71.     int dumblroot();
  72. #endif
  73.  
  74.  
  75. /*
  76.  * Standard pico keymenus...
  77.  */
  78. static KEYMENU menu_pico[] = {
  79.     {"^G", "Get Help", KS_SCREENHELP},    {"^O", "WriteOut", KS_SAVEFILE},
  80.     {"^R", "Read File", KS_READFILE},    {"^Y", "Prev Pg", KS_PREVPAGE},
  81.     {"^K", "Cut Text", KS_NONE},    {"^C", "Cur Pos", KS_CURPOSITION},
  82.     {"^X", "Exit", KS_EXIT},        {"^J", "Justify", KS_JUSTIFY},
  83.     {"^W", "Where is", KS_WHEREIS},    {"^V", "Next Pg", KS_NEXTPAGE},
  84.     {"^U", NULL, KS_NONE},
  85. #ifdef    SPELLER
  86.     {"^T", "To Spell", KS_SPELLCHK}
  87. #else
  88.     {"^D", "Del Char", KS_NONE}
  89. #endif
  90. };
  91. #define    UNCUT_KEY    10
  92.  
  93.  
  94. static KEYMENU menu_compose[] = {
  95.     {"^G", "Get Help", KS_SCREENHELP},    {"^X", NULL, KS_SEND},
  96.     {"^R", "Read File", KS_READFILE},    {"^Y", "Prev Pg", KS_PREVPAGE},
  97.     {"^K", "Cut Text", KS_NONE},    {"^O", "Postpone", KS_POSTPONE},
  98.     {"^C", "Cancel", KS_CANCEL},    {"^J", "Justify", KS_JUSTIFY},
  99.     {NULL, NULL, KS_NONE},        {"^V", "Next Pg", KS_NEXTPAGE},
  100.     {"^U", NULL, KS_NONE},
  101. #ifdef    SPELLER
  102.     {"^T", "To Spell", KS_SPELLCHK}
  103. #else
  104.     {"^D", "Del Char", KS_NONE}
  105. #endif
  106. };
  107. #define    EXIT_KEY    1
  108. #define    PSTPN_KEY    5
  109. #define    WHERE_KEY    8
  110.  
  111.  
  112. /*
  113.  * Definition's for pico's modeline
  114.  */
  115. #define    PICO_TITLE    "  UW PICO(tm) %s  "
  116. #define    PICO_MOD_MSG    "Modified  "
  117. #define    PICO_NEWBUF_MSG    " New Buffer "
  118.  
  119. #define WFDEBUG 0                       /* Window flag debug. */
  120.  
  121. typedef struct  VIDEO {
  122.         short   v_flag;                 /* Flags */
  123.         CELL    v_text[1];              /* Screen data. */
  124. }       VIDEO;
  125.  
  126. #define VFCHG   0x0001                  /* Changed flag            */
  127. #define    VFEXT    0x0002            /* extended (beyond column 80)    */
  128. #define    VFREV    0x0004            /* reverse video status        */
  129. #define    VFREQ    0x0008            /* reverse video request    */
  130.  
  131. int     vtrow   = 0;                    /* Row location of SW cursor */
  132. int     vtcol   = 0;                    /* Column location of SW cursor */
  133. int     ttrow   = HUGE;                 /* Row location of HW cursor */
  134. int     ttcol   = HUGE;                 /* Column location of HW cursor */
  135. int    lbound    = 0;            /* leftmost column of current line
  136.                        being displayed */
  137.  
  138. VIDEO   **vscreen;                      /* Virtual screen. */
  139. VIDEO   **pscreen;                      /* Physical screen. */
  140.  
  141.  
  142. /*
  143.  * Initialize the data structures used by the display code. The edge vectors
  144.  * used to access the screens are set up. The operating system's terminal I/O
  145.  * channel is set up. All the other things get initialized at compile time.
  146.  * The original window has "WFCHG" set, so that it will get completely
  147.  * redrawn on the first call to "update".
  148.  */
  149. vtinit()
  150. {
  151.     register int i;
  152.     register VIDEO *vp;
  153.  
  154.     (*term.t_open)();
  155.  
  156.     (*term.t_rev)(FALSE);
  157.     vscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
  158.     if (vscreen == NULL){
  159.     emlwrite("Allocating memory for virtual display failed.", NULL);
  160.         return(FALSE);
  161.     }
  162.  
  163.  
  164.     pscreen = (VIDEO **) malloc((term.t_nrow+1)*sizeof(VIDEO *));
  165.     if (pscreen == NULL){
  166.     free((void *)vscreen);
  167.     emlwrite("Allocating memory for physical display failed.", NULL);
  168.         return(FALSE);
  169.     }
  170.  
  171.  
  172.     for (i = 0; i <= term.t_nrow; ++i) {
  173.         vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  174.  
  175.         if (vp == NULL){
  176.         free((void *)vscreen);
  177.         free((void *)pscreen);
  178.         emlwrite("Allocating memory for virtual display lines failed.",
  179.              NULL);
  180.             return(FALSE);
  181.     }
  182.     else
  183.       memset(vp, ' ', sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  184.  
  185.     vp->v_flag = 0;
  186.         vscreen[i] = vp;
  187.         vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  188.  
  189.         if (vp == NULL){
  190.             free((void *)vscreen[i]);
  191.         while(--i >= 0){
  192.         free((void *)vscreen[i]);
  193.         free((void *)pscreen[i]);
  194.         }
  195.  
  196.         free((void *)vscreen);
  197.         free((void *)pscreen);
  198.         emlwrite("Allocating memory for physical display lines failed.",
  199.              NULL);
  200.             return(FALSE);
  201.     }
  202.     else
  203.       memset(vp, ' ', sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  204.  
  205.     vp->v_flag = 0;
  206.         pscreen[i] = vp;
  207.     }
  208.  
  209.     return(TRUE);
  210. }
  211.  
  212.  
  213. /*
  214.  * Clean up the virtual terminal system, in anticipation for a return to the
  215.  * operating system. Move down to the last line and clear it out (the next
  216.  * system prompt will be written in the line). Shut down the channel to the
  217.  * terminal.
  218.  */
  219. vttidy()
  220. {
  221.     movecursor(term.t_nrow-1, 0);
  222.     peeol();
  223.     movecursor(term.t_nrow, 0);
  224.     peeol();
  225.     (*term.t_close)();
  226. }
  227.  
  228.  
  229. /*
  230.  * Set the virtual cursor to the specified row and column on the virtual
  231.  * screen. There is no checking for nonsense values; this might be a good
  232.  * idea during the early stages.
  233.  */
  234. vtmove(row, col)
  235. int row, col;
  236. {
  237.     vtrow = row;
  238.     vtcol = col;
  239. }
  240.  
  241.  
  242. /*
  243.  * Write a character to the virtual screen. The virtual row and column are
  244.  * updated. If the line is too long put a "$" in the last column. This routine
  245.  * only puts printing characters into the virtual terminal buffers. Only
  246.  * column overflow is checked.
  247.  */
  248. vtputc(c)
  249. CELL c;
  250. {
  251.     register VIDEO      *vp;
  252.     CELL     ac;
  253.  
  254.     vp = vscreen[vtrow];
  255.     ac.c = ' ';
  256.     ac.a = c.a;
  257.  
  258.     if (vtcol >= term.t_ncol) {
  259.         vtcol = (vtcol + 0x07) & ~0x07;
  260.     ac.c = '$';
  261.         vp->v_text[term.t_ncol - 1] = ac;
  262.     }
  263.     else if (c.c == '\t') {
  264.         do {
  265.             vtputc(ac);
  266.     }
  267.         while ((vtcol&0x07) != 0);
  268.     }
  269.     else if (c.c < 0x20 || c.c == 0x7F) {
  270.     ac.c = '^';
  271.         vtputc(ac);
  272.     ac.c = (c.c ^ 0x40);
  273.         vtputc(ac);
  274.     }
  275.     else
  276.       vp->v_text[vtcol++] = c;
  277. }
  278.  
  279.  
  280. /* put a character to the virtual screen in an extended line. If we are
  281.  * not yet on left edge, don't print it yet. check for overflow on
  282.  * the right margin.
  283.  */
  284. vtpute(c)
  285. CELL c;
  286. {
  287.     register VIDEO      *vp;
  288.     CELL                 ac;
  289.  
  290.     vp = vscreen[vtrow];
  291.     ac.c = ' ';
  292.     ac.a = c.a;
  293.  
  294.     if (vtcol >= term.t_ncol) {
  295.         vtcol = (vtcol + 0x07) & ~0x07;
  296.     ac.c = '$';
  297.         vp->v_text[term.t_ncol - 1] = ac;
  298.     }
  299.     else if (c.c == '\t'){
  300.         do {
  301.             vtpute(ac);
  302.         }
  303.         while (((vtcol + lbound)&0x07) != 0 && vtcol < term.t_ncol);
  304.     }
  305.     else if (c.c < 0x20 || c.c == 0x7F) {
  306.     ac.c = '^';
  307.         vtpute(ac);
  308.     ac.c = (c.c ^ 0x40);
  309.         vtpute(ac);
  310.     }
  311.     else {
  312.     if (vtcol >= 0)
  313.       vp->v_text[vtcol] = c;
  314.     ++vtcol;
  315.     }
  316. }
  317.  
  318.  
  319. /*
  320.  * Erase from the end of the software cursor to the end of the line on which
  321.  * the software cursor is located.
  322.  */
  323. vteeol()
  324. {
  325.     register VIDEO      *vp;
  326.     CELL     c;
  327.  
  328.     c.c = ' ';
  329.     c.a = 0;
  330.     vp = vscreen[vtrow];
  331.     while (vtcol < term.t_ncol)
  332.       vp->v_text[vtcol++] = c;
  333. }
  334.  
  335.  
  336. /*
  337.  * Make sure that the display is right. This is a three part process. First,
  338.  * scan through all of the windows looking for dirty ones. Check the framing,
  339.  * and refresh the screen. Second, make sure that "currow" and "curcol" are
  340.  * correct for the current window. Third, make the virtual and physical
  341.  * screens the same.
  342.  */
  343. update()
  344. {
  345.     register LINE   *lp;
  346.     register WINDOW *wp;
  347.     register VIDEO  *vp1;
  348.     register VIDEO  *vp2;
  349.     register int     i;
  350.     register int     j;
  351.     register int     scroll = 0;
  352.     CELL         c;
  353.  
  354. #if    TYPEAH
  355.     if (typahead())
  356.     return(TRUE);
  357. #endif
  358.  
  359. #ifdef _WINDOWS
  360.     /* This tells our MS Windows module to not bother updating the
  361.      * cursor position while a massive screen update is in progress.
  362.      */
  363.     mswin_beginupdate ();
  364. #endif
  365.  
  366. /*
  367.  * BUG: setting and unsetting whole region at a time is dumb.  fix this.
  368.  */
  369.     if(curwp->w_markp){
  370.     unmarkbuffer();
  371.     markregion(1);
  372.     }
  373.  
  374.     wp = wheadp;
  375.  
  376.     while (wp != NULL){
  377.         /* Look at any window with update flags set on. */
  378.  
  379.         if (wp->w_flag != 0){
  380.             /* If not force reframe, check the framing. */
  381.  
  382.             if ((wp->w_flag & WFFORCE) == 0){
  383.                 lp = wp->w_linep;
  384.  
  385.                 for (i = 0; i < wp->w_ntrows; ++i){
  386.                     if (lp == wp->w_dotp)
  387.               goto out;
  388.  
  389.                     if (lp == wp->w_bufp->b_linep)
  390.               break;
  391.  
  392.                     lp = lforw(lp);
  393.         }
  394.         }
  395.  
  396.             /* Not acceptable, better compute a new value for the line at the
  397.              * top of the window. Then set the "WFHARD" flag to force full
  398.              * redraw.
  399.              */
  400.             i = wp->w_force;
  401.  
  402.             if (i > 0){
  403.                 --i;
  404.  
  405.                 if (i >= wp->w_ntrows)
  406.                   i = wp->w_ntrows-1;
  407.         }
  408.             else if (i < 0){
  409.                 i += wp->w_ntrows;
  410.  
  411.                 if (i < 0)
  412.           i = 0;
  413.         }
  414.             else if(optimize){
  415.         /* 
  416.          * find dotp, if its been moved just above or below the 
  417.          * window, use scrollxxx() to facilitate quick redisplay...
  418.          */
  419.         lp = lforw(wp->w_dotp);
  420.         if(lp != wp->w_dotp){
  421.             if(lp == wp->w_linep && lp != wp->w_bufp->b_linep){
  422.             scroll = 1;
  423.             }
  424.             else {
  425.             lp = wp->w_linep;
  426.             for(j=0;j < wp->w_ntrows; ++j){
  427.                 if(lp != wp->w_bufp->b_linep)
  428.                   lp = lforw(lp);
  429.                 else
  430.                   break;
  431.             }
  432.             if(lp == wp->w_dotp && j == wp->w_ntrows)
  433.               scroll = 2;
  434.             }
  435.         }
  436.         j = i = wp->w_ntrows/2;
  437.         }
  438.         else
  439.           i = wp->w_ntrows/2;
  440.  
  441.             lp = wp->w_dotp;
  442.  
  443.             while (i != 0 && lback(lp) != wp->w_bufp->b_linep){
  444.                 --i;
  445.                 lp = lback(lp);
  446.         }
  447.  
  448.         /*
  449.          * this is supposed to speed things up by using tcap sequences
  450.          * to efficiently scroll the terminal screen.  the thinking here
  451.          * is that its much faster to update pscreen[] than to actually
  452.          * write the stuff to the screen...
  453.          */
  454.         if(optimize){
  455.         switch(scroll){
  456.           case 1:            /* scroll text down */
  457.             j = j-i+1;            /* add one for dot line */
  458.             /* 
  459.              * do we scroll down the header as well?  Well, only 
  460.              * if we're not editing the header, we've backed up 
  461.              * to the top, and the composer is not being 
  462.              * displayed...
  463.              */
  464.             if(Pmaster && Pmaster->headents && !ComposerEditing 
  465.                && (lback(lp) == wp->w_bufp->b_linep)
  466.                && (ComposerTopLine == COMPOSER_TOP_LINE))
  467.               j += entry_line(1000, TRUE); /* Never > 1000 headers */
  468.  
  469.             scrolldown(wp, -1, j);
  470.             break;
  471.           case 2:            /* scroll text up */
  472.             j = wp->w_ntrows - (j-i);    /* we chose new top line! */
  473.             if(Pmaster && j){
  474.             /* 
  475.              * do we scroll down the header as well?  Well, only 
  476.              * if we're not editing the header, we've backed up 
  477.              * to the top, and the composer is not being 
  478.              * displayed...
  479.              */
  480.             if(!ComposerEditing 
  481.                && (ComposerTopLine != COMPOSER_TOP_LINE))
  482.               scrollup(wp, COMPOSER_TOP_LINE, 
  483.                    j+entry_line(1000, TRUE));
  484.             else
  485.               scrollup(wp, -1, j);
  486.             }
  487.             else
  488.               scrollup(wp, -1, j);
  489.             break;
  490.             default :
  491.               break;
  492.         }
  493.         }
  494.  
  495.             wp->w_linep = lp;
  496.             wp->w_flag |= WFHARD;       /* Force full. */
  497. out:
  498.         /*
  499.          * if the line at the top of the page is the top line
  500.          * in the body, show the header...
  501.          */
  502.         if(Pmaster && Pmaster->headents && !ComposerEditing){
  503.         if(lback(wp->w_linep) == wp->w_bufp->b_linep){
  504.             if(ComposerTopLine == COMPOSER_TOP_LINE){
  505.             i = term.t_nrow - 2 - term.t_mrow - HeaderLen();
  506.             if(i > 0 && nlforw() >= i) {    /* room for header ? */
  507.                 if((i = nlforw()/2) == 0 && term.t_nrow&1)
  508.                   i = 1;
  509.                 while(wp->w_linep != wp->w_bufp->b_linep && i--)
  510.                   wp->w_linep = lforw(wp->w_linep);
  511.                 
  512.             }
  513.             else
  514.               ToggleHeader(1);
  515.             }
  516.         }
  517.         else{
  518.             if(ComposerTopLine != COMPOSER_TOP_LINE)
  519.               ToggleHeader(0);        /* hide it ! */
  520.         }
  521.         }
  522.  
  523.             /* Try to use reduced update. Mode line update has its own special
  524.              * flag. The fast update is used if the only thing to do is within
  525.              * the line editing.
  526.              */
  527.             lp = wp->w_linep;
  528.             i = wp->w_toprow;
  529.  
  530.             if ((wp->w_flag & ~WFMODE) == WFEDIT){
  531.                 while (lp != wp->w_dotp){
  532.                     ++i;
  533.                     lp = lforw(lp);
  534.         }
  535.  
  536.                 vscreen[i]->v_flag |= VFCHG;
  537.                 vtmove(i, 0);
  538.  
  539.                 for (j = 0; j < llength(lp); ++j)
  540.                     vtputc(lgetc(lp, j));
  541.  
  542.                 vteeol();
  543.         }
  544.         else if ((wp->w_flag & (WFEDIT | WFHARD)) != 0){
  545.                 while (i < wp->w_toprow+wp->w_ntrows){
  546.                     vscreen[i]->v_flag |= VFCHG;
  547.                     vtmove(i, 0);
  548.  
  549.             /* if line has been changed */
  550.                     if (lp != wp->w_bufp->b_linep){
  551.                         for (j = 0; j < llength(lp); ++j)
  552.                             vtputc(lgetc(lp, j));
  553.  
  554.                         lp = lforw(lp);
  555.             }
  556.  
  557.                     vteeol();
  558.                     ++i;
  559.         }
  560.         }
  561. #if ~WFDEBUG
  562.             if ((wp->w_flag&WFMODE) != 0)
  563.                 modeline(wp);
  564.  
  565.             wp->w_flag  = 0;
  566.             wp->w_force = 0;
  567. #endif
  568.     }
  569. #if WFDEBUG
  570.         modeline(wp);
  571.         wp->w_flag =  0;
  572.         wp->w_force = 0;
  573. #endif
  574.  
  575.     /* and onward to the next window */
  576.         wp = wp->w_wndp;
  577.     }
  578.  
  579.     /* Always recompute the row and column number of the hardware cursor. This
  580.      * is the only update for simple moves.
  581.      */
  582.     lp = curwp->w_linep;
  583.     currow = curwp->w_toprow;
  584.  
  585.     while (lp != curwp->w_dotp){
  586.         ++currow;
  587.         lp = lforw(lp);
  588.     }
  589.  
  590.     curcol = 0;
  591.     i = 0;
  592.  
  593.     while (i < curwp->w_doto){
  594.     c = lgetc(lp, i++);
  595.  
  596.         if (c.c == '\t')
  597.             curcol |= 0x07;
  598.         else if (c.c < 0x20 || c.c == 0x7F)
  599.             ++curcol;
  600.  
  601.         ++curcol;
  602.     }
  603.  
  604.     if (curcol >= term.t_ncol) {         /* extended line. */
  605.     /* flag we are extended and changed */
  606.     vscreen[currow]->v_flag |= VFEXT | VFCHG;
  607.     updext();                /* and output extended line */
  608.     } else
  609.       lbound = 0;                /* not extended line */
  610.  
  611.     /* make sure no lines need to be de-extended because the cursor is
  612.      * no longer on them 
  613.      */
  614.  
  615.     wp = wheadp;
  616.  
  617.     while (wp != NULL) {
  618.     lp = wp->w_linep;
  619.     i = wp->w_toprow;
  620.  
  621.     while (i < wp->w_toprow + wp->w_ntrows) {
  622.         if (vscreen[i]->v_flag & VFEXT) {
  623.         /* always flag extended lines as changed */
  624.         vscreen[i]->v_flag |= VFCHG;
  625.         if ((wp != curwp) || (lp != wp->w_dotp) ||
  626.             (curcol < term.t_ncol)) {
  627.             vtmove(i, 0);
  628.             for (j = 0; j < llength(lp); ++j)
  629.               vtputc(lgetc(lp, j));
  630.             vteeol();
  631.  
  632.             /* this line no longer is extended */
  633.             vscreen[i]->v_flag &= ~VFEXT;
  634.         }
  635.         }
  636.         lp = lforw(lp);
  637.         ++i;
  638.     }
  639.     /* and onward to the next window */
  640.         wp = wp->w_wndp;
  641.     }
  642.  
  643.     /* Special hacking if the screen is garbage. Clear the hardware screen,
  644.      * and update your copy to agree with it. Set all the virtual screen
  645.      * change bits, to force a full update.
  646.      */
  647.  
  648.     if (sgarbf != FALSE){
  649.     if(Pmaster){
  650.         showCompTitle();
  651.  
  652.         if(ComposerTopLine != COMPOSER_TOP_LINE){
  653.         UpdateHeader();            /* arrange things */
  654.         PaintHeader(COMPOSER_TOP_LINE, TRUE);
  655.         }
  656.  
  657.         /*
  658.          * since we're using only a portion of the screen and only 
  659.          * one buffer, only clear enough screen for the current window
  660.          * which is to say the *only* window.
  661.          */
  662.         for(i=wheadp->w_toprow;i<=term.t_nrow; i++){
  663.         movecursor(i, 0);
  664.         peeol();
  665.         vscreen[i]->v_flag |= VFCHG;
  666.         }
  667.         (*Pmaster->showmsg)('X' & 0x1f);    /* ctrl-L */
  668.         movecursor(wheadp->w_toprow, 0);
  669.     }
  670.     else{
  671.         for (i = 0; i < term.t_nrow-term.t_mrow; ++i){
  672.         vscreen[i]->v_flag |= VFCHG;
  673.         vp1 = pscreen[i];
  674.         c.c = ' ';
  675.         c.a = 0;
  676.         for (j = 0; j < term.t_ncol; ++j)
  677.           vp1->v_text[j] = c;
  678.         }
  679.  
  680.         movecursor(0, 0);                   /* Erase the screen. */
  681.         (*term.t_eeop)();
  682.  
  683.     }
  684.  
  685.         sgarbf = FALSE;                /* Erase-page clears */
  686.         mpresf = FALSE;                /* the message area. */
  687.  
  688.     if(Pmaster)
  689.       modeline(curwp);
  690.     else
  691.       sgarbk = TRUE;            /* fix the keyhelp as well...*/
  692.     }
  693.  
  694.     /* Make sure that the physical and virtual displays agree. Unlike before,
  695.      * the "updateline" code is only called with a line that has been updated
  696.      * for sure.
  697.      */
  698.     if(Pmaster)
  699.       i = curwp->w_toprow;
  700.     else
  701.       i = 0;
  702.  
  703.     c.c = term.t_nrow - term.t_mrow;
  704.  
  705.     for (; i < (int)c.c; ++i){
  706.  
  707.         vp1 = vscreen[i];
  708.  
  709.     /* for each line that needs to be updated, or that needs its
  710.        reverse video status changed, call the line updater    */
  711.     j = vp1->v_flag;
  712.         if (j & VFCHG){
  713.  
  714. #if    TYPEAH
  715.         if (typahead()){
  716. #ifdef _WINDOWS
  717.         mswin_endupdate ();
  718. #endif
  719.             return(TRUE);
  720.         }
  721. #endif
  722.             vp2 = pscreen[i];
  723.  
  724.             updateline(i, &vp1->v_text[0], &vp2->v_text[0], &vp1->v_flag);
  725.  
  726.     }
  727.     }
  728.  
  729.     if(Pmaster == NULL){
  730.  
  731.     if(sgarbk != FALSE){
  732.         if(term.t_mrow > 0){
  733.         movecursor(term.t_nrow-1, 0);
  734.         peeol();
  735.         movecursor(term.t_nrow, 0);
  736.         peeol();
  737.         }
  738.  
  739.         if(lastflag&CFFILL){
  740.         menu_pico[UNCUT_KEY].label = "UnJustify";
  741.         emlwrite("Can now UnJustify!", NULL);
  742.         mpresf = HUGE;    /* remove this after next keystroke! */
  743.         }
  744.         else
  745.           menu_pico[UNCUT_KEY].label = "UnCut Text";
  746.  
  747.         wkeyhelp(menu_pico);
  748.         sgarbk = FALSE;
  749.         }
  750.     }
  751.  
  752.     /* Finally, update the hardware cursor and flush out buffers. */
  753.  
  754.     movecursor(currow, curcol - lbound);
  755. #ifdef _WINDOWS
  756.     mswin_endupdate ();
  757.  
  758.     /* 
  759.      * Update the scroll bars.  This function is where curbp->b_linecnt
  760.      * is really managed.  See update_scroll.
  761.      */
  762.     update_scroll ();
  763. #endif
  764.     (*term.t_flush)();
  765. }
  766.  
  767.  
  768. /* updext - update the extended line which the cursor is currently
  769.  *        on at a column greater than the terminal width. The line
  770.  *        will be scrolled right or left to let the user see where
  771.  *        the cursor is
  772.  */
  773. updext()
  774. {
  775.     register int rcursor;        /* real cursor location */
  776.     register LINE *lp;            /* pointer to current line */
  777.     register int j;            /* index into line */
  778.  
  779.     /* calculate what column the real cursor will end up in */
  780.     rcursor = ((curcol - term.t_ncol) % term.t_scrsiz) + term.t_margin;
  781.     lbound = curcol - rcursor + 1;
  782.  
  783.     /* scan through the line outputing characters to the virtual screen
  784.      * once we reach the left edge
  785.      */
  786.     vtmove(currow, -lbound);        /* start scanning offscreen */
  787.     lp = curwp->w_dotp;            /* line to output */
  788.     for (j=0; j<llength(lp); ++j)    /* until the end-of-line */
  789.       vtpute(lgetc(lp, j));
  790.  
  791.     /* truncate the virtual line */
  792.     vteeol();
  793.  
  794.     /* and put a '$' in column 1 */
  795.     vscreen[currow]->v_text[0].c = '$';
  796.     vscreen[currow]->v_text[0].a = 0;
  797. }
  798.  
  799.  
  800. /*
  801.  * Update a single line. This does not know how to use insert or delete
  802.  * character sequences; we are using VT52 functionality. Update the physical
  803.  * row and column variables. It does try an exploit erase to end of line. The
  804.  * RAINBOW version of this routine uses fast video.
  805.  */
  806. updateline(row, vline, pline, flags)
  807. int  row;
  808. CELL vline[];                /* what we want it to end up as */
  809. CELL pline[];                /* what it looks like now       */
  810. short *flags;                /* and how we want it that way  */
  811. {
  812.     register CELL *cp1;
  813.     register CELL *cp2;
  814.     register CELL *cp3;
  815.     register CELL *cp4;
  816.     register CELL *cp5;
  817.     register CELL *cp6;
  818.     register CELL *cp7;
  819.     register int  display = TRUE;
  820.     register int nbflag;        /* non-blanks to the right flag? */
  821.  
  822.  
  823.     /* set up pointers to virtual and physical lines */
  824.     cp1 = &vline[0];
  825.     cp2 = &pline[0];
  826.     cp3 = &vline[term.t_ncol];
  827.  
  828.     /* advance past any common chars at the left */
  829.     while (cp1 != cp3 && cp1[0].c == cp2[0].c && cp1[0].a == cp2[0].a) {
  830.     ++cp1;
  831.     ++cp2;
  832.     }
  833.  
  834. /* This can still happen, even though we only call this routine on changed
  835.  * lines. A hard update is always done when a line splits, a massive
  836.  * change is done, or a buffer is displayed twice. This optimizes out most
  837.  * of the excess updating. A lot of computes are used, but these tend to
  838.  * be hard operations that do a lot of update, so I don't really care.
  839.  */
  840.     /* if both lines are the same, no update needs to be done */
  841.     if (cp1 == cp3){
  842.     *flags &= ~VFCHG;            /* mark it clean */
  843.     return(TRUE);
  844.     }
  845.  
  846.     /* find out if there is a match on the right */
  847.     nbflag = FALSE;
  848.     cp3 = &vline[term.t_ncol];
  849.     cp4 = &pline[term.t_ncol];
  850.  
  851.     while (cp3[-1].c == cp4[-1].c && cp3[-1].a == cp4[-1].a) {
  852.     --cp3;
  853.     --cp4;
  854.     if (cp3[0].c != ' ' || cp3[0].a != 0)/* Note if any nonblank */
  855.       nbflag = TRUE;        /* in right match. */
  856.     }
  857.  
  858.     cp5 = cp3;
  859.  
  860.     if (nbflag == FALSE && eolexist == TRUE) {    /* Erase to EOL ? */
  861.     while (cp5 != cp1 && cp5[-1].c == ' ' && cp5[-1].a == 0)
  862.       --cp5;
  863.  
  864.     if (cp3-cp5 <= 3)        /* Use only if erase is */
  865.       cp5 = cp3;            /* fewer characters. */
  866.     }
  867.  
  868.     movecursor(row, cp1-&vline[0]);        /* Go to start of line. */
  869.  
  870.     if (!nbflag) {                /* use insert or del char? */
  871.     cp6 = cp3;
  872.     cp7 = cp4;
  873.  
  874.     if(inschar&&(cp7!=cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a)){
  875.         while (cp7 != cp2 && cp6[0].c==cp7[-1].c && cp6[0].a==cp7[-1].a){
  876.         --cp7;
  877.         --cp6;
  878.         }
  879.  
  880.         if (cp7==cp2 && cp4-cp2 > 3){
  881.         o_insert((char)cp1->c);     /* insert the char */
  882.         display = FALSE;        /* only do it once!! */
  883.         }
  884.     }
  885.     else if(delchar && cp3 != cp1 && cp7[0].c == cp6[-1].c
  886.         && cp7[0].a == cp6[-1].a){
  887.         while (cp6 != cp1 && cp7[0].c==cp6[-1].c && cp7[0].a==cp6[-1].a){
  888.         --cp7;
  889.         --cp6;
  890.         }
  891.  
  892.         if (cp6==cp1 && cp5-cp6 > 3){
  893.         o_delete();        /* insert the char */
  894.         display = FALSE;        /* only do it once!! */
  895.         }
  896.     }
  897.     }
  898.  
  899.     while (cp1 != cp5) {        /* Ordinary. */
  900.     if(display){
  901.         (*term.t_rev)(cp1->a);    /* set inverse for this char */
  902.         (*term.t_putchar)(cp1->c);
  903.     }
  904.  
  905.     ++ttcol;
  906.     *cp2++ = *cp1++;
  907.     }
  908.  
  909.     (*term.t_rev)(0);            /* turn off inverse anyway! */
  910.  
  911.     if (cp5 != cp3) {            /* Erase. */
  912.     if(display)
  913.       peeol();
  914.     while (cp1 != cp3)
  915.       *cp2++ = *cp1++;
  916.     }
  917.     *flags &= ~VFCHG;            /* flag this line is changed */
  918. }
  919.  
  920.  
  921. /*
  922.  * Redisplay the mode line for the window pointed to by the "wp". This is the
  923.  * only routine that has any idea of how the modeline is formatted. You can
  924.  * change the modeline format by hacking at this routine. Called by "update"
  925.  * any time there is a dirty window.
  926.  */
  927. modeline(wp)
  928. WINDOW *wp;
  929. {
  930.     if(Pmaster){
  931.         if(ComposerEditing)
  932.       ShowPrompt();
  933.     else{
  934.         menu_compose[EXIT_KEY].label  = (Pmaster->headents)
  935.                           ? "Send" :"Exit";
  936.         menu_compose[PSTPN_KEY].name  = (Pmaster->headents)
  937.                           ? "^O" : NULL;
  938.         menu_compose[PSTPN_KEY].label = (Pmaster->headents)
  939.                           ? "Postpone" : NULL;
  940.         menu_compose[WHERE_KEY].name  = (Pmaster->alt_ed) ? "^_" : "^W";
  941.         menu_compose[WHERE_KEY].label = (Pmaster->alt_ed) ? "Alt Edit" 
  942.                                   : "Where is";
  943.         KS_OSDATASET(&menu_compose[WHERE_KEY],
  944.              (Pmaster->alt_ed) ? KS_ALTEDITOR : KS_WHEREIS);
  945.         menu_compose[UNCUT_KEY].label = (thisflag&CFFILL) ? "UnJustify"
  946.                                   : "UnCut Text";
  947.         wkeyhelp(menu_compose);
  948. #ifdef _WINDOWS
  949.         /* When alt editor is available "Where is" is not on the menu
  950.          * but the command is still available.  This call enables any
  951.          * "Where is" menu items. */
  952.         if (Pmaster->alt_ed)
  953.         mswin_menuitemadd (MENU|CTRL|'W', "", KS_WHEREIS, 0);
  954. #endif 
  955.     }
  956.     }
  957.     else{
  958.     register char *cp;
  959.     register int n;        /* cursor position count */
  960.     register BUFFER *bp;
  961.     register i;        /* loop index */
  962.     register lchar;        /* character to draw line in buffer with */
  963.     char     tline[NLINE];    /* buffer for part of mode line */
  964.     CELL     c;
  965.  
  966.     n = 0;
  967.     c.a = 1;
  968.     vtmove(1, 0);
  969.     vteeol();
  970.     vscreen[n]->v_flag |= VFCHG; /* Redraw next time. */
  971.     vtmove(n, 0);        /* Seek to right line. */
  972.  
  973. #if    REVSTA
  974.     if (revexist)
  975.       lchar = ' ';
  976.     else
  977. #endif
  978.       lchar = '-';
  979.  
  980.     c.c = lchar;
  981.     vtputc(c);
  982.     bp = wp->w_bufp;
  983.  
  984.     n = 1;
  985.  
  986.     sprintf(cp = tline, PICO_TITLE, version);    /* write version */
  987.  
  988.     while(c.c = *cp++){
  989.         vtputc(c);
  990.         ++n;
  991.     }
  992.  
  993.     if(bp->b_fname[0]){                /* File name? */
  994.         char *p, *endp, *prefix;
  995.  
  996.         prefix = NULL;                /* for abreviation */
  997.         endp   = strchr(cp = bp->b_fname, '\0');    /* find eol */
  998.         i       = term.t_ncol - n - 22;        /* space available */
  999.         while(i > 0 && endp - cp > i){
  1000.         if(!prefix){
  1001.             prefix = ".../";            /* path won't fit! */
  1002.             i -= 4;
  1003.         }
  1004.  
  1005.         if(!(p = strchr(cp, '/'))){        /* neat break? */
  1006.             cp = endp - i;            /* do best we can */
  1007.             break;
  1008.         }
  1009.         else
  1010.           cp = p + 1;
  1011.         }
  1012.  
  1013.         sprintf(tline, "%*.sFile: %s%s",
  1014.             ((i - (endp - cp)) > 0) ? (i - (endp - cp))/2 : 0, " ",
  1015.             prefix ? prefix : "", cp);
  1016.  
  1017.         for(cp = tline; c.c = *cp; cp++, n++)
  1018.           vtputc(c);
  1019.         }
  1020.         else{
  1021.         cp = PICO_NEWBUF_MSG;
  1022.         if(sizeof(PICO_NEWBUF_MSG) < term.t_ncol){    /* enough room? */
  1023.         c.c = lchar;
  1024.         for(i = (term.t_ncol-sizeof(PICO_NEWBUF_MSG))/2; n < i; n++)
  1025.           vtputc(c);
  1026.         }
  1027.  
  1028.         while(c.c = *cp++){
  1029.         vtputc(c);
  1030.         ++n;
  1031.         }
  1032.     }
  1033.  
  1034. #if WFDEBUG
  1035.     vtputc(lchar);
  1036.     vtputc((wp->w_flag&WFMODE)!=0  ? 'M' : lchar);
  1037.     vtputc((wp->w_flag&WFHARD)!=0  ? 'H' : lchar);
  1038.     vtputc((wp->w_flag&WFEDIT)!=0  ? 'E' : lchar);
  1039.     vtputc((wp->w_flag&WFMOVE)!=0  ? 'V' : lchar);
  1040.     vtputc((wp->w_flag&WFFORCE)!=0 ? 'F' : lchar);
  1041.     n += 6;
  1042. #endif
  1043.     i = term.t_ncol - n;                /* space available */
  1044.     cp = NULL;
  1045.     if(bp->b_flag&BFCHG){                /* "MOD" if changed. */
  1046.         cp = PICO_MOD_MSG;
  1047.         i  = (i > sizeof(PICO_MOD_MSG)) ? i - sizeof(PICO_MOD_MSG) : 0;
  1048.     }
  1049.  
  1050.     c.c = lchar;
  1051.     while(i-- > 0)                    /* Pad width */
  1052.       vtputc(c);
  1053.  
  1054.     if(cp)
  1055.       while(c.c = *cp++)
  1056.         vtputc(c);
  1057.     }
  1058. }
  1059.  
  1060.  
  1061.  
  1062. /*
  1063.  * Send a command to the terminal to move the hardware cursor to row "row"
  1064.  * and column "col". The row and column arguments are origin 0. Optimize out
  1065.  * random calls. Update "ttrow" and "ttcol".
  1066.  */
  1067. movecursor(row, col)
  1068. int row, col;
  1069. {
  1070.     if (row!=ttrow || col!=ttcol) {
  1071.         ttrow = row;
  1072.         ttcol = col;
  1073.         (*term.t_move)(row, col);
  1074.     }
  1075. }
  1076.  
  1077.  
  1078. /*
  1079.  * Erase any sense we have of the cursor's HW location...
  1080.  */
  1081. clearcursor()
  1082. {
  1083.     ttrow = ttcol = HUGE;
  1084. }
  1085.  
  1086.  
  1087. /*
  1088.  * Erase the message line. This is a special routine because the message line
  1089.  * is not considered to be part of the virtual screen. It always works
  1090.  * immediately; the terminal buffer is flushed via a call to the flusher.
  1091.  */
  1092. mlerase()
  1093. {
  1094.     movecursor(term.t_nrow - term.t_mrow, 0);
  1095.     (*term.t_rev)(0);
  1096.     if (eolexist == TRUE)
  1097.       peeol();
  1098.     else
  1099.       while(++ttcol < term.t_ncol)        /* track's ttcol */
  1100.     (*term.t_putchar)(' ');
  1101.  
  1102.     (*term.t_flush)();
  1103.     mpresf = FALSE;
  1104. }
  1105.  
  1106.  
  1107. /*
  1108.  * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
  1109.  * ABORT. The ABORT status is returned if the user bumps out of the question
  1110.  * with a ^G. if d >= 0, d is the default answer returned. Otherwise there
  1111.  * is no default.
  1112.  */
  1113. mlyesno(prompt, dflt)
  1114. char  *prompt;
  1115. int   dflt;
  1116. {
  1117.     int     rv;
  1118.     char    buf[NLINE];
  1119.     KEYMENU menu_yesno[12];
  1120.  
  1121. #ifdef _WINDOWS
  1122.     if (mswin_usedialog ()) 
  1123.       switch (mswin_yesno (prompt)) {
  1124.     default:
  1125.     case 0:        return (ABORT);
  1126.     case 1:        return (TRUE);
  1127.     case 2:        return (FALSE);
  1128.       }
  1129. #endif  
  1130.  
  1131.     for(rv = 0; rv < 12; rv++){
  1132.     menu_yesno[rv].name = NULL;
  1133.     KS_OSDATASET(&menu_yesno[rv], KS_NONE);
  1134.     }
  1135.  
  1136.     menu_yesno[1].name  = "Y";
  1137.     menu_yesno[1].label = (dflt == TRUE) ? "[Yes]" : "Yes";
  1138.     menu_yesno[6].name  = "^C";
  1139.     menu_yesno[6].label = "Cancel";
  1140.     menu_yesno[7].name  = "N";
  1141.     menu_yesno[7].label = (dflt == FALSE) ? "[No]" : "No";
  1142.     wkeyhelp(menu_yesno);        /* paint generic menu */
  1143.     sgarbk = TRUE;            /* mark menu dirty */
  1144.     if(Pmaster && curwp)
  1145.       curwp->w_flag |= WFMODE;
  1146.  
  1147.     sprintf(buf, "%s ? ", prompt);
  1148.     mlwrite(buf, NULL);
  1149.     (*term.t_rev)(1);
  1150.     rv = -1;
  1151.     while(1){
  1152.     switch(GetKey()){
  1153.       case (CTRL|'M') :        /* default */
  1154.         if(dflt >= 0){
  1155.         pputs((dflt) ? "Yes" : "No", 1);
  1156.         rv = dflt;
  1157.         }
  1158.         else
  1159.           (*term.t_beep)();
  1160.  
  1161.         break;
  1162.  
  1163.       case (CTRL|'C') :        /* Bail out! */
  1164.       case F2         :
  1165.         pputs("ABORT", 1);
  1166.         rv = ABORT;
  1167.         break;
  1168.  
  1169.       case 'y' :
  1170.       case 'Y' :
  1171.       case F3  :
  1172.         pputs("Yes", 1);
  1173.         rv = TRUE;
  1174.         break;
  1175.  
  1176.       case 'n' :
  1177.       case 'N' :
  1178.       case F4  :
  1179.         pputs("No", 1);
  1180.         rv = FALSE;
  1181.         break;
  1182.  
  1183.       case (CTRL|'G') :
  1184.         if(term.t_mrow == 0 && km_popped == 0){
  1185.         movecursor(term.t_nrow-2, 0);
  1186.         peeol();
  1187.         term.t_mrow = 2;
  1188.         (*term.t_rev)(0);
  1189.         wkeyhelp(menu_yesno);        /* paint generic menu */
  1190.         mlwrite(buf, NULL);
  1191.         (*term.t_rev)(1);
  1192.         sgarbk = TRUE;            /* mark menu dirty */
  1193.         km_popped++;
  1194.         break;
  1195.         }
  1196.         /* else fall through */
  1197.  
  1198.       default:
  1199.         (*term.t_beep)();
  1200.       case NODATA :
  1201.         break;
  1202.     }
  1203.  
  1204.     (*term.t_flush)();
  1205.     if(rv != -1){
  1206.         (*term.t_rev)(0);
  1207.         if(km_popped){
  1208.         term.t_mrow = 0;
  1209.         movecursor(term.t_nrow, 0);
  1210.         peeol();
  1211.         sgarbf = 1;
  1212.         km_popped = 0;
  1213.         }
  1214.  
  1215.         return(rv);
  1216.     }
  1217.     }
  1218. }
  1219.  
  1220.  
  1221.  
  1222. /*
  1223.  * Write a prompt into the message line, then read back a response. Keep
  1224.  * track of the physical position of the cursor. If we are in a keyboard
  1225.  * macro throw the prompt away, and return the remembered response. This
  1226.  * lets macros run at full speed. The reply is always terminated by a carriage
  1227.  * return. Handle erase, kill, and abort keys.
  1228.  */
  1229. mlreply(prompt, buf, nbuf, flg, extras)
  1230. char      *prompt, *buf;
  1231. int       nbuf, flg;
  1232. EXTRAKEYS *extras;
  1233. {
  1234.     return(mlreplyd(prompt, buf, nbuf, flg|QDEFLT, extras));
  1235. }
  1236.  
  1237.  
  1238. /*
  1239.  * function key mappings
  1240.  */
  1241. static int rfkm[12][2] = {
  1242.     { F1,  (CTRL|'G')},
  1243.     { F2,  (CTRL|'C')},
  1244.     { F3,  0 },
  1245.     { F4,  0 },
  1246.     { F5,  0 },
  1247.     { F6,  0 },
  1248.     { F7,  0 },
  1249.     { F8,  0 },
  1250.     { F9,  0 },
  1251.     { F10, 0 },
  1252.     { F11, 0 },
  1253.     { F12, 0 }
  1254. };
  1255.  
  1256.  
  1257. /*
  1258.  * mlreplyd - write the prompt to the message line along with an default
  1259.  *          answer already typed in.  Carraige return accepts the
  1260.  *          default.  answer returned in buf which also holds the initial
  1261.  *            default, nbuf is it's length, def set means use default value,
  1262.  *            and ff means for-file which checks that all chars are allowed
  1263.  *            in file names.
  1264.  */
  1265. mlreplyd(prompt, buf, nbuf, flg, extras)
  1266. char      *prompt;
  1267. char      *buf;
  1268. int       nbuf, flg;
  1269. EXTRAKEYS *extras;
  1270. {
  1271.     register int    c;                /* current char       */
  1272.     register char   *b;                /* pointer in buf     */
  1273.     register int    i, j;
  1274.     register int    maxl;
  1275.     register int    plen;
  1276.     int      changed = FALSE;
  1277.     int      return_val = 0;
  1278.     KEYMENU  menu_mlreply[12];
  1279.     int         extra_v[12];
  1280.  
  1281. #ifdef _WINDOWS
  1282.     if (mswin_usedialog ()) {
  1283.     MDlgButton        btn_list[12];
  1284.     int            i, j;
  1285.  
  1286.     memset (&btn_list, 0, sizeof (MDlgButton) * 12);
  1287.     j = 0;
  1288.     for (i = 0; extras && extras[i].name != NULL; ++i) {
  1289.         if (extras[i].label[0] != '\0') {
  1290.         if ((extras[i].key & CTRL) == CTRL) 
  1291.             btn_list[j].ch = (extras[i].key & ~CTRL) - '@';
  1292.         else
  1293.             btn_list[j].ch = extras[i].key;
  1294.         btn_list[j].rval = extras[i].key;
  1295.         btn_list[j].name = extras[i].name;
  1296.         btn_list[j++].label = extras[i].label;
  1297.         }
  1298.     }
  1299.     btn_list[j].ch = -1;
  1300.  
  1301.     return (mswin_dialog (prompt, buf, nbuf, ((flg&QDEFLT) > 0), 
  1302.             FALSE, btn_list, NULL, 0));
  1303.     }
  1304. #endif
  1305.  
  1306.     menu_mlreply[0].name = "^G";
  1307.     menu_mlreply[0].label = "Get Help";
  1308.     KS_OSDATASET(&menu_mlreply[0], KS_SCREENHELP);
  1309.     for(j = 0, i = 1; i < 6; i++){    /* insert odd extras */
  1310.     menu_mlreply[i].name = NULL;
  1311.     KS_OSDATASET(&menu_mlreply[i], KS_NONE);
  1312.     rfkm[2*i][1] = 0;
  1313.     if(extras){
  1314.         for(; extras[j].name && j != 2*(i-1); j++)
  1315.           ;
  1316.  
  1317.         if(extras[j].name){
  1318.         rfkm[2*i][1]          = extras[j].key;
  1319.         menu_mlreply[i].name  = extras[j].name;
  1320.         menu_mlreply[i].label = extras[j].label;
  1321.         KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
  1322.         }
  1323.     }
  1324.     }
  1325.  
  1326.     menu_mlreply[6].name = "^C";
  1327.     menu_mlreply[6].label = "Cancel";
  1328.     KS_OSDATASET(&menu_mlreply[6], KS_NONE);
  1329.     for(j = 0, i = 7; i < 12; i++){    /* insert even extras */
  1330.     menu_mlreply[i].name = NULL;
  1331.     rfkm[2*(i-6)+1][1] = 0;
  1332.     if(extras){
  1333.         for(; extras[j].name && j != (2*(i-6)) - 1; j++)
  1334.           ;
  1335.  
  1336.         if(extras[j].name){
  1337.         rfkm[2*(i-6)+1][1]    = extras[j].key;
  1338.         menu_mlreply[i].name  = extras[j].name;
  1339.         menu_mlreply[i].label = extras[j].label;
  1340.         KS_OSDATASET(&menu_mlreply[i], KS_OSDATAGET(&extras[j]));
  1341.         }
  1342.     }
  1343.     }
  1344.  
  1345.     /* set up what to watch for and return values */
  1346.     memset(extra_v, 0, sizeof(extra_v));
  1347.     for(i = 0, j = 0; i < 12 && extras && extras[i].name; i++)
  1348.       extra_v[j++] = extras[i].key;
  1349.  
  1350.     mlwrite(prompt, NULL);        /* paint prompt */
  1351.     plen = strlen(prompt);
  1352.     if(!(flg&QDEFLT))
  1353.       *buf = '\0';
  1354.  
  1355.     (*term.t_rev)(1);
  1356.  
  1357.     maxl = (nbuf < term.t_ncol - plen - 1) ? nbuf : term.t_ncol - plen - 1;
  1358.  
  1359.     pputs(buf, 1);
  1360.     b = &buf[(flg & QBOBUF) ? 0 : strlen(buf)];
  1361.     
  1362.     (*term.t_rev)(0);
  1363.     wkeyhelp(menu_mlreply);        /* paint generic menu */
  1364.     sgarbk = 1;                /* mark menu dirty */
  1365.     (*term.t_rev)(1);
  1366.  
  1367.     for (;;) {
  1368.     movecursor(term.t_nrow - term.t_mrow, plen + b - buf);
  1369.     (*term.t_flush)();
  1370.  
  1371.  
  1372.     while((c = GetKey()) == NODATA)
  1373.       ;
  1374.  
  1375.     switch(c = normalize_cmd(c, rfkm, 1)){
  1376.       case (CTRL|'A') :            /* CTRL-A beginning     */
  1377.       case K_PAD_HOME :
  1378.         b = buf;
  1379.         continue;
  1380.  
  1381.       case (CTRL|'B') :            /* CTRL-B back a char   */
  1382.         if(ttcol > plen)
  1383.         b--;
  1384.         continue;
  1385.  
  1386.       case (CTRL|'C') :            /* CTRL-C abort        */
  1387.         pputs("ABORT", 1);
  1388.         ctrlg(FALSE, 0);
  1389.         (*term.t_rev)(0);
  1390.         (*term.t_flush)();
  1391.         return_val = ABORT;
  1392.         goto ret;
  1393.  
  1394.       case (CTRL|'E') :            /* CTRL-E end of line   */
  1395.       case K_PAD_END  :
  1396.         b = &buf[strlen(buf)];
  1397.         continue;
  1398.  
  1399.       case (CTRL|'F') :            /* CTRL-F forward a char*/
  1400.         if(*b != '\0')
  1401.         b++;
  1402.         continue;
  1403.  
  1404.       case (CTRL|'G') :            /* CTRL-G help        */
  1405.         if(term.t_mrow == 0 && km_popped == 0){
  1406.         movecursor(term.t_nrow-2, 0);
  1407.         peeol();
  1408.         sgarbk = 1;            /* mark menu dirty */
  1409.         km_popped++;
  1410.         term.t_mrow = 2;
  1411.         (*term.t_rev)(0);
  1412.         wkeyhelp(menu_mlreply);        /* paint generic menu */
  1413.         mlwrite(prompt, NULL);        /* paint prompt */
  1414.         plen = strlen(prompt);
  1415.         (*term.t_rev)(1);
  1416.         pputs(buf, 1);
  1417.         break;
  1418.         }
  1419.  
  1420.         pputs("HELP", 1);
  1421.         (*term.t_rev)(0);
  1422.         (*term.t_flush)();
  1423.         return_val = HELPCH;
  1424.         goto ret;
  1425.  
  1426.       case (CTRL|'H') :            /* CTRL-H backspace    */
  1427.       case 0x7f :                /*        rubout    */
  1428.         if (b <= buf)
  1429.           break;
  1430.         b--;
  1431.         ttcol--;                /* cheating!  no pputc */
  1432.         (*term.t_putchar)('\b');
  1433.  
  1434.       case (CTRL|'D') :            /* CTRL-D delete char   */
  1435.       case K_PAD_DELETE :
  1436.         changed=TRUE;
  1437.         i = 0;
  1438.         do                    /* blat out left char   */
  1439.           b[i] = b[i+1];
  1440.         while(b[i++] != '\0');
  1441.         break;
  1442.  
  1443.       case (CTRL|'L') :            /* CTRL-L redraw    */
  1444.         (*term.t_rev)(0);
  1445.         return_val = (CTRL|'L');
  1446.         goto ret;
  1447.  
  1448.       case (CTRL|'K') :            /* CTRL-K kill line    */
  1449.         changed=TRUE;
  1450.         buf[0] = '\0';
  1451.         b = buf;
  1452.         movecursor(ttrow, plen);
  1453.         break;
  1454.  
  1455.       case K_PAD_LEFT:
  1456.         if(ttcol > plen)
  1457.           b--;
  1458.         continue;
  1459.  
  1460.       case K_PAD_RIGHT:
  1461.         if(*b != '\0')
  1462.           b++;
  1463.         continue;
  1464.  
  1465.       case F1 :                /* sort of same thing */
  1466.         (*term.t_rev)(0);
  1467.         (*term.t_flush)();
  1468.         return_val = HELPCH;
  1469.         goto ret;
  1470.  
  1471.       case (CTRL|'M') :            /*        newline       */
  1472.         (*term.t_rev)(0);
  1473.         (*term.t_flush)();
  1474.         return_val = changed;
  1475.         goto ret;
  1476.  
  1477.       default : 
  1478.         if(strlen(buf) >= maxl){        /* contain the text      */
  1479.         (*term.t_beep)();
  1480.         continue;
  1481.         }
  1482.  
  1483.         /* look for match in extra_v */
  1484.         for(i = 0; i < 12; i++)
  1485.           if(c && c == extra_v[i]){
  1486.           (*term.t_rev)(0);
  1487.           return_val = c;
  1488.           goto ret;
  1489.           }
  1490.  
  1491.         changed=TRUE;
  1492.  
  1493.         if(c&(~0xff)){            /* bag ctrl/special chars */
  1494.         (*term.t_beep)();
  1495.         }
  1496.         else{
  1497.         i = strlen(b);
  1498.         if(flg&QFFILE){
  1499.             if(!fallowc(c)){         /* c OK in filename? */
  1500.             (*term.t_beep)();
  1501.             continue;
  1502.             }
  1503.         }
  1504.  
  1505.         do                /* blat out left char   */
  1506.           b[i+1] = b[i];
  1507.         while(i-- > 0);
  1508.  
  1509.         pputc(*b++ = c, 0);
  1510.         }
  1511.     }
  1512.  
  1513.     pputs(b, 1);                /* show default */
  1514.     i = term.t_ncol-1;
  1515.     while(pscreen[ttrow]->v_text[i].c == ' ' 
  1516.           && pscreen[ttrow]->v_text[i].a == 0)
  1517.       i--;
  1518.  
  1519.     while(ttcol <= i)
  1520.       pputc(' ', 0);
  1521.     }
  1522.  
  1523. ret:
  1524.     if(km_popped){
  1525.     term.t_mrow = 0;
  1526.     movecursor(term.t_nrow, 0);
  1527.     peeol();
  1528.     sgarbf = 1;
  1529.     km_popped = 0;
  1530.     }
  1531.  
  1532.     return(return_val);
  1533. }
  1534.  
  1535.  
  1536. /*
  1537.  * emlwrite() - write the message string to the error half of the screen
  1538.  *              center justified.  much like mlwrite (which is still used
  1539.  *              to paint the line for prompts and such), except it center
  1540.  *              the text.
  1541.  */
  1542. void
  1543. emlwrite(message, arg) 
  1544. char    *message;
  1545. void    *arg;
  1546. {
  1547.     register char *bufp = message;
  1548.     register char *ap;
  1549.     register long l;
  1550.  
  1551.     mlerase();
  1552.  
  1553.     if((l = strlen(message)) == 0)        /* nothing to write, bag it */
  1554.       return;
  1555.  
  1556.     /*
  1557.      * next, figure out where the to move the cursor so the message 
  1558.      * comes out centered
  1559.      */
  1560.     if((ap=(char *)strchr(message, '%')) != NULL){
  1561.     l -= 2;
  1562.     switch(ap[1]){
  1563.       case '%':
  1564.       case 'c':
  1565.         l += 1;
  1566.         break;
  1567.       case 'd':
  1568.         l += (long)dumbroot((int)arg, 10);
  1569.         break;
  1570.       case 'D':
  1571.         l += (long)dumblroot((long)arg, 10);
  1572.         break;
  1573.       case 'o':
  1574.         l += (long)dumbroot((int)arg, 8);
  1575.         break;
  1576.       case 'x':
  1577.         l += (long)dumbroot((int)arg, 16);
  1578.         break;
  1579.       case 's':
  1580.             l += strlen((char *)arg);
  1581.         break;
  1582.     }
  1583.     }
  1584.  
  1585.     if(l-4 <= term.t_ncol)            /* this wouldn't be good */
  1586.       movecursor(term.t_nrow-term.t_mrow, (term.t_ncol - (int)l - 4)/2);
  1587.     else
  1588.       movecursor(term.t_nrow-term.t_mrow, 0);
  1589.  
  1590.     (*term.t_rev)(1);
  1591.     pputs("[ ", 1);
  1592.     while (*bufp != '\0' && ttcol < term.t_ncol-2){
  1593.     if(*bufp == '\007')
  1594.       (*term.t_beep)();
  1595.     else if(*bufp == '%'){
  1596.         switch(*++bufp){
  1597.           case 'c':
  1598.         pputc((char)(int)arg, 0);
  1599.         break;
  1600.           case 'd':
  1601.         mlputi((int)arg, 10);
  1602.         break;
  1603.           case 'D':
  1604.         mlputli((long)arg, 10);
  1605.         break;
  1606.           case 'o':
  1607.         mlputi((int)arg, 16);
  1608.         break;
  1609.           case 'x':
  1610.         mlputi((int)arg, 8);
  1611.         break;
  1612.           case 's':
  1613.         pputs((char *)arg, 0);
  1614.         break;
  1615.           case '%':
  1616.           default:
  1617.         pputc(*bufp, 0);
  1618.         break;
  1619.         }
  1620.     }
  1621.     else
  1622.       pputc(*bufp, 0);
  1623.     bufp++;
  1624.     }
  1625.  
  1626.     pputs(" ]", 1);
  1627.     (*term.t_rev)(0);
  1628.     (*term.t_flush)();
  1629.     mpresf = TRUE;
  1630. }
  1631.  
  1632.  
  1633. /*
  1634.  * Write a message into the message line. Keep track of the physical cursor
  1635.  * position. A small class of printf like format items is handled. Assumes the
  1636.  * stack grows down; this assumption is made by the "++" in the argument scan
  1637.  * loop. Set the "message line" flag TRUE.
  1638.  */
  1639. mlwrite(fmt, arg)
  1640. char *fmt;
  1641. void *arg;
  1642. {
  1643.     register int c;
  1644.     register char *ap;
  1645.  
  1646.     /*
  1647.      * the idea is to only highlight if there is something to show
  1648.      */
  1649.     mlerase();
  1650.  
  1651.     ttcol = 0;
  1652.     (*term.t_rev)(1);
  1653.     ap = (char *) &arg;
  1654.     while ((c = *fmt++) != 0) {
  1655.         if (c != '%') {
  1656.             (*term.t_putchar)(c);
  1657.             ++ttcol;
  1658.     }
  1659.         else {
  1660.             c = *fmt++;
  1661.             switch (c) {
  1662.           case 'd':
  1663.         mlputi(*(int *)ap, 10);
  1664.         ap += sizeof(int);
  1665.         break;
  1666.  
  1667.           case 'o':
  1668.         mlputi(*(int *)ap,  8);
  1669.         ap += sizeof(int);
  1670.         break;
  1671.  
  1672.           case 'x':
  1673.         mlputi(*(int *)ap, 16);
  1674.         ap += sizeof(int);
  1675.         break;
  1676.  
  1677.           case 'D':
  1678.         mlputli(*(long *)ap, 10);
  1679.         ap += sizeof(long);
  1680.         break;
  1681.  
  1682.           case 's':
  1683.         pputs(*(char **)ap, 1);
  1684.         ap += sizeof(char *);
  1685.         break;
  1686.  
  1687.               default:
  1688.         (*term.t_putchar)(c);
  1689.         ++ttcol;
  1690.         }
  1691.     }
  1692.     }
  1693.  
  1694.     c = ttcol;
  1695.     while(ttcol < term.t_ncol)
  1696.       pputc(' ', 0);
  1697.  
  1698.     movecursor(term.t_nrow - term.t_mrow, c);
  1699.     (*term.t_rev)(0);
  1700.     (*term.t_flush)();
  1701.     mpresf = TRUE;
  1702. }
  1703.  
  1704.  
  1705. /*
  1706.  * Write out an integer, in the specified radix. Update the physical cursor
  1707.  * position. This will not handle any negative numbers; maybe it should.
  1708.  */
  1709. mlputi(i, r)
  1710. int i, r;
  1711. {
  1712.     register int q;
  1713.     static char hexdigits[] = "0123456789ABCDEF";
  1714.  
  1715.     if (i < 0){
  1716.         i = -i;
  1717.     pputc('-', 1);
  1718.     }
  1719.  
  1720.     q = i/r;
  1721.  
  1722.     if (q != 0)
  1723.       mlputi(q, r);
  1724.  
  1725.     pputc(hexdigits[i%r], 1);
  1726. }
  1727.  
  1728.  
  1729. /*
  1730.  * do the same except as a long integer.
  1731.  */
  1732. mlputli(l, r)
  1733. long l;
  1734. int  r;
  1735. {
  1736.     register long q;
  1737.  
  1738.     if (l < 0){
  1739.         l = -l;
  1740.         pputc('-', 1);
  1741.     }
  1742.  
  1743.     q = l/r;
  1744.  
  1745.     if (q != 0)
  1746.       mlputli(q, r);
  1747.  
  1748.     pputc((int)(l%r)+'0', 1);
  1749. }
  1750.  
  1751.  
  1752. /*
  1753.  * scrolldown - use stuff to efficiently move blocks of text on the
  1754.  *              display, and update the pscreen array to reflect those
  1755.  *              moves...
  1756.  *
  1757.  *        wp is the window to move in
  1758.  *        r  is the row at which to begin scrolling
  1759.  *        n  is the number of lines to scrol
  1760.  */
  1761. scrolldown(wp, r, n)
  1762. WINDOW *wp;
  1763. int     r, n;
  1764. {
  1765. #ifdef    TERMCAP
  1766.     register int i;
  1767.     register int l;
  1768.     register VIDEO *vp1;
  1769.     register VIDEO *vp2;
  1770.  
  1771.     if(!n)
  1772.       return;
  1773.  
  1774.     if(r < 0){
  1775.     r = wp->w_toprow;
  1776.     l = wp->w_ntrows;
  1777.     }
  1778.     else{
  1779.     if(r > wp->w_toprow)
  1780.         vscreen[r-1]->v_flag |= VFCHG;
  1781.     l = wp->w_toprow+wp->w_ntrows-r;
  1782.     }
  1783.  
  1784.     o_scrolldown(r, n);
  1785.  
  1786.     for(i=l-n-1; i >=  0; i--){
  1787.     vp1 = pscreen[r+i]; 
  1788.     vp2 = pscreen[r+i+n];
  1789.     bcopy(vp1, vp2, term.t_ncol * sizeof(CELL));
  1790.     }
  1791.     pprints(r+n-1, r);
  1792.     ttrow = HUGE;
  1793.     ttcol = HUGE;
  1794. #endif /* TERMCAP */
  1795. }
  1796.  
  1797.  
  1798. /*
  1799.  * scrollup - use tcap stuff to efficiently move blocks of text on the
  1800.  *            display, and update the pscreen array to reflect those
  1801.  *            moves...
  1802.  */
  1803. scrollup(wp, r, n)
  1804. WINDOW *wp;
  1805. int     r, n;
  1806. {
  1807. #ifdef    TERMCAP
  1808.     register int i;
  1809.     register VIDEO *vp1;
  1810.     register VIDEO *vp2;
  1811.  
  1812.     if(!n)
  1813.       return;
  1814.  
  1815.     if(r < 0)
  1816.       r = wp->w_toprow;
  1817.  
  1818.     o_scrollup(r, n);
  1819.  
  1820.     i = 0;
  1821.     while(1){
  1822.     if(Pmaster){
  1823.         if(!(r+i+n < wp->w_toprow+wp->w_ntrows))
  1824.           break;
  1825.     }
  1826.     else{
  1827.         if(!((i < wp->w_ntrows-n)&&(r+i+n < wp->w_toprow+wp->w_ntrows)))
  1828.           break;
  1829.     }
  1830.     vp1 = pscreen[r+i+n]; 
  1831.     vp2 = pscreen[r+i];
  1832.     bcopy(vp1, vp2, term.t_ncol * sizeof(CELL));
  1833.     i++;
  1834.     }
  1835.     pprints(wp->w_toprow+wp->w_ntrows-n, wp->w_toprow+wp->w_ntrows-1);
  1836.     ttrow = HUGE;
  1837.     ttcol = HUGE;
  1838. #endif /* TERMCAP */
  1839. }
  1840.  
  1841.  
  1842. /*
  1843.  * print spaces in the physical screen starting from row abs(n) working in
  1844.  * either the positive or negative direction (depending on sign of n).
  1845.  */
  1846. pprints(x, y)
  1847. int x, y;
  1848. {
  1849.     register int i;
  1850.     register int j;
  1851.  
  1852.     if(x < y){
  1853.     for(i = x;i <= y; ++i){
  1854.         for(j = 0; j < term.t_ncol; j++){
  1855.         pscreen[i]->v_text[j].c = ' ';
  1856.         pscreen[i]->v_text[j].a = 0;
  1857.         }
  1858.         }
  1859.     }
  1860.     else{
  1861.     for(i = x;i >= y; --i){
  1862.         for(j = 0; j < term.t_ncol; j++){
  1863.         pscreen[i]->v_text[j].c = ' ';
  1864.         pscreen[i]->v_text[j].a = 0;
  1865.         }
  1866.         }
  1867.     }
  1868.     ttrow = y;
  1869.     ttcol = 0;
  1870. }
  1871.  
  1872.  
  1873.  
  1874. /*
  1875.  * doton - return the physical line number that the dot is on in the
  1876.  *         current window, and by side effect the number of lines remaining
  1877.  */
  1878. doton(r, chs)
  1879. int       *r;
  1880. unsigned  *chs;
  1881. {
  1882.     register int  i = 0;
  1883.     register LINE *lp = curwp->w_linep;
  1884.     int      l = -1;
  1885.  
  1886.     *chs = 0;
  1887.     while(i++ < curwp->w_ntrows){
  1888.     if(lp == curwp->w_dotp)
  1889.       l = i-1;
  1890.     lp = lforw(lp);
  1891.     if(lp == curwp->w_bufp->b_linep){
  1892.         i++;
  1893.         break;
  1894.     }
  1895.     if(l >= 0)
  1896.       (*chs) += llength(lp);
  1897.     }
  1898.     *r = i - l - term.t_mrow;
  1899.     return(l+curwp->w_toprow);
  1900. }
  1901.  
  1902.  
  1903.  
  1904. /*
  1905.  * resize_pico - given new window dimensions, allocate new resources
  1906.  */
  1907. resize_pico(row, col)
  1908. int  row, col;
  1909. {
  1910.     int old_nrow, old_ncol;
  1911.     register int i;
  1912.     register VIDEO *vp;
  1913.  
  1914.     old_nrow = term.t_nrow;
  1915.     old_ncol = term.t_ncol;
  1916.  
  1917.     term.t_nrow = row;
  1918.     term.t_ncol = col;
  1919.  
  1920.     if (old_ncol == term.t_ncol && old_nrow == term.t_nrow)
  1921.       return(TRUE);
  1922.  
  1923.     if(curwp){
  1924.     curwp->w_toprow = 2;
  1925.     curwp->w_ntrows = term.t_nrow - curwp->w_toprow - term.t_mrow;
  1926.     }
  1927.  
  1928.     if(Pmaster){
  1929.     fillcol = Pmaster->fillcolumn;
  1930.     (*Pmaster->resize)();
  1931.     }
  1932.     else if(userfillcol > 0)
  1933.       fillcol = userfillcol;
  1934.     else
  1935.       fillcol = term.t_ncol - 6;           /* we control the fill column */
  1936.  
  1937.     /* 
  1938.      * free unused screen space ...
  1939.      */
  1940.     for(i=term.t_nrow+1; i <= old_nrow; ++i){
  1941.     free((char *) vscreen[i]);
  1942.     free((char *) pscreen[i]);
  1943.     }
  1944.  
  1945.     /* 
  1946.      * realloc new space for screen ...
  1947.      */
  1948.     if((vscreen=(VIDEO **)realloc(vscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
  1949.     if(Pmaster)
  1950.       return(-1);
  1951.     else
  1952.       exit(1);
  1953.     }
  1954.  
  1955.     if((pscreen=(VIDEO **)realloc(pscreen,(term.t_nrow+1)*sizeof(VIDEO *))) == NULL){
  1956.     if(Pmaster)
  1957.       return(-1);
  1958.     else
  1959.       exit(1);
  1960.     }
  1961.  
  1962.     for (i = 0; i <= term.t_nrow; ++i) {
  1963.     if(i <= old_nrow)
  1964.       vp = (VIDEO *) realloc(vscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1965.     else
  1966.       vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1967.  
  1968.     if (vp == NULL)
  1969.       exit(1);
  1970.     vp->v_flag = VFCHG;
  1971.     vscreen[i] = vp;
  1972.     if(old_ncol < term.t_ncol){  /* don't let any garbage in */
  1973.         vtrow = i;
  1974.         vtcol = (i < old_nrow) ? old_ncol : 0;
  1975.         vteeol();
  1976.     }
  1977.  
  1978.     if(i <= old_nrow)
  1979.       vp = (VIDEO *) realloc(pscreen[i], sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1980.     else
  1981.       vp = (VIDEO *) malloc(sizeof(VIDEO)+(term.t_ncol*sizeof(CELL)));
  1982.  
  1983.     if (vp == NULL)
  1984.       exit(1);
  1985.  
  1986.     vp->v_flag = VFCHG;
  1987.     pscreen[i] = vp;
  1988.     }
  1989.  
  1990.     if(!ResizeBrowser()){
  1991.     if(Pmaster && Pmaster->headents){
  1992.         ResizeHeader();
  1993.     }
  1994.     else{
  1995.         lchange(WFHARD);                   /* set update flags... */
  1996.         curwp->w_flag |= WFMODE;           /* and modeline so we  */
  1997.         refresh(0, 1);                     /* redraw the whole enchilada. */
  1998.         update();                          /* do it */
  1999.     }
  2000.     }
  2001.  
  2002.     return(TRUE);
  2003. }
  2004.  
  2005.  
  2006. /*
  2007.  * showCompTitle - display the anchor line passed in from pine
  2008.  */
  2009. showCompTitle()
  2010. {
  2011.     if(Pmaster){
  2012.     register char *bufp;
  2013.     extern   char *pico_anchor;
  2014.  
  2015.     if((bufp = pico_anchor) == NULL)
  2016.       return(1);
  2017.  
  2018.     movecursor(COMPOSER_TITLE_LINE, 0);
  2019.     (*term.t_rev)(1);   
  2020.     while (ttcol < term.t_ncol)
  2021.       if(*bufp != '\0')
  2022.         pputc(*bufp++, 1);
  2023.           else
  2024.         pputc(' ', 1);
  2025.  
  2026.     (*term.t_rev)(0);
  2027.     movecursor(COMPOSER_TITLE_LINE + 1, 0);
  2028.     peeol();
  2029.     }
  2030. }
  2031.  
  2032.  
  2033.  
  2034. /*
  2035.  * zotdisplay - blast malloc'd space created for display maps
  2036.  */
  2037. zotdisplay()
  2038. {
  2039.     register int i;
  2040.  
  2041.     for (i = 0; i <= term.t_nrow; ++i){        /* free screens */
  2042.     free((char *) vscreen[i]);
  2043.     free((char *) pscreen[i]);
  2044.     }
  2045.  
  2046.     free((char *) vscreen);
  2047.     free((char *) pscreen);
  2048. }
  2049.  
  2050.  
  2051.  
  2052. /*
  2053.  * nlforw() - returns the number of lines from the top to the dot
  2054.  */
  2055. nlforw()
  2056. {
  2057.     register int  i = 0;
  2058.     register LINE *lp = curwp->w_linep;
  2059.     
  2060.     while(lp != curwp->w_dotp){
  2061.     lp = lforw(lp);
  2062.     i++;
  2063.     }
  2064.     return(i);
  2065. }
  2066.  
  2067.  
  2068.  
  2069. /*
  2070.  * pputc - output the given char, keep track of it on the physical screen
  2071.  *       array, and keep track of the cursor
  2072.  */
  2073. pputc(c, a)
  2074. int   c;                /* char to write */
  2075. int   a;                /* and its attribute */
  2076. {
  2077.     if((ttcol >= 0 && ttcol < term.t_ncol) 
  2078.        && (ttrow >= 0 && ttrow <= term.t_nrow)){
  2079. /*    (*term.t_rev)(a);*/
  2080.     (*term.t_putchar)(c);            /* write it */
  2081. /*    (*term.t_rev)(!a);*/
  2082.     pscreen[ttrow]->v_text[ttcol].c = c;    /* keep track of it */
  2083.     pscreen[ttrow]->v_text[ttcol++].a = a;    /* keep track of it */
  2084.     }
  2085. }
  2086.  
  2087.  
  2088. /*
  2089.  * pputs - print a string and keep track of the cursor
  2090.  */
  2091. pputs(s, a)
  2092. register char *s;            /* string to write */
  2093. register int   a;            /* and its attribute */
  2094. {
  2095.     while (*s != '\0')
  2096.       pputc(*s++, a);
  2097. }
  2098.  
  2099.  
  2100. /*
  2101.  * peeol - physical screen array erase to end of the line.  remember to
  2102.  *       track the cursor.
  2103.  */
  2104. peeol()
  2105. {
  2106.     register int r = ttrow;
  2107.     register int c = ttcol;
  2108.     CELL         cl;
  2109.  
  2110.     cl.c = ' ';
  2111.     cl.a = 0;
  2112.     (*term.t_eeol)();
  2113.     while(c < term.t_ncol && c >= 0 && r <= term.t_nrow && r >= 0)
  2114.       pscreen[r]->v_text[c++] = cl;
  2115. }
  2116.  
  2117.  
  2118. /*
  2119.  * pscr - return the character cell on the physical screen map on the 
  2120.  *        given line, l, and offset, o.
  2121.  */
  2122. CELL *
  2123. pscr(l, o)
  2124. int l, o;
  2125. {
  2126.     if((l >= 0 && l <= term.t_nrow) && (o >= 0 && o < term.t_ncol))
  2127.       return(&(pscreen[l]->v_text[o]));
  2128.     else
  2129.       return(NULL);
  2130. }
  2131.  
  2132.  
  2133. /*
  2134.  * pclear() - clear the physical screen from row x through row y
  2135.  */
  2136. pclear(x, y)
  2137. register int x;
  2138. register int y;
  2139. {
  2140.     register int i;
  2141.  
  2142.     for(i=x; i < y; i++){
  2143.     movecursor(i, 0);
  2144.     peeol();
  2145.     }
  2146. }
  2147.  
  2148.  
  2149. /*
  2150.  * dumbroot - just get close 
  2151.  */
  2152. dumbroot(x, b)
  2153. int x, b;
  2154. {
  2155.     if(x < b)
  2156.       return(1);
  2157.     else
  2158.       return(dumbroot(x/b, b) + 1);
  2159. }
  2160.  
  2161.  
  2162. /*
  2163.  * dumblroot - just get close 
  2164.  */
  2165. dumblroot(x, b)
  2166. long x;
  2167. int  b;
  2168. {
  2169.     if(x < b)
  2170.       return(1);
  2171.     else
  2172.       return(dumblroot(x/b, b) + 1);
  2173. }
  2174.  
  2175.  
  2176. /*
  2177.  * pinsertc - use optimized insert, fixing physical screen map.
  2178.  *            returns true if char written, false otherwise
  2179.  */
  2180. pinsert(c)
  2181. CELL c;
  2182. {
  2183.     register int   i;
  2184.     register CELL *p;
  2185.  
  2186.     if(o_insert((unsigned char) c.c)){    /* if we've got it, use it! */
  2187.     p = pscreen[ttrow]->v_text;    /* then clean up physical screen */
  2188.     for(i = term.t_ncol-1; i > ttcol; i--)
  2189.       p[i] = p[i-1];        /* shift right */
  2190.  
  2191.     p[ttcol++] = c;            /* insert new char */
  2192.     
  2193.     return(1);
  2194.     }
  2195.  
  2196.     return(0);
  2197. }
  2198.  
  2199.  
  2200. /*
  2201.  * pdel - use optimized delete to rub out the current char and
  2202.  *        fix the physical screen array.
  2203.  *        returns true if optimized the delete, false otherwise
  2204.  */
  2205. pdel()
  2206. {
  2207.     register int   i;
  2208.     register CELL *c;
  2209.  
  2210.     if(delchar){            /* if we've got it, use it! */
  2211.     (*term.t_putchar)('\b');     /* move left a char */
  2212.     --ttcol;
  2213.     o_delete();            /* and delete it */
  2214.  
  2215.     c = pscreen[ttrow]->v_text;    /* then clean up physical screen */
  2216.     for(i=ttcol; i < term.t_ncol; i++)
  2217.       c[i] = c[i+1];
  2218.     c[i].c = ' ';
  2219.     c[i].a = 0;
  2220.     
  2221.     return(1);
  2222.     }
  2223.  
  2224.     return(0);
  2225. }
  2226.  
  2227.  
  2228.  
  2229. /*
  2230.  * wstripe - write out the given string at the given location, and reverse
  2231.  *           video on flagged characters.  Does the same thing as pine's
  2232.  *           stripe.
  2233.  */
  2234. void
  2235. wstripe(line, column, pmt, key)
  2236. int    line, column;
  2237. char    *pmt;
  2238. int      key;
  2239. {
  2240.     register char *buf;
  2241.     register int  i = 0;
  2242.     register int  j = 0;
  2243.     register int  l;
  2244.  
  2245.     l = strlen(pmt);
  2246.     while(1){
  2247.     if(i >= term.t_ncol || j >= l)
  2248.       return;                /* equal strings */
  2249.  
  2250.     if(pmt[j] == key)
  2251.       j++;
  2252.  
  2253.     if(pscr(line, i)->c != pmt[j]){
  2254.         if(j >= 1 && pmt[j-1] == key)
  2255.           j--;
  2256.         break;
  2257.     }
  2258.  
  2259.     j++;
  2260.     i++;
  2261.     }
  2262.  
  2263.     movecursor(line, column+i);
  2264.     buf = &pmt[j];
  2265.     do{
  2266.     if(*buf == key){
  2267.         buf++;
  2268.         (*term.t_rev)(1);
  2269.         pputc(*buf, 1);
  2270.         (*term.t_rev)(0);
  2271.     }
  2272.     else{
  2273.         pputc(*buf, 0);
  2274.     }
  2275.     }    
  2276.     while(*++buf != '\0');
  2277.     peeol();
  2278.     (*term.t_flush)();
  2279. }
  2280.  
  2281.  
  2282.  
  2283. /*
  2284.  *  wkeyhelp - paint list of possible commands on the bottom
  2285.  *             of the display (yet another pine clone)
  2286.  *  NOTE: function key mode is handled here since all the labels
  2287.  *        are the same...
  2288.  */
  2289. wkeyhelp(keymenu)
  2290. KEYMENU *keymenu;
  2291. {
  2292.     char *obufp, *p, fkey[4], linebuf[NLINE];
  2293.     int   row, slot, tspace, nspace[6], index, n;
  2294. #ifdef    MOUSE
  2295.     char  nbuf[NLINE];
  2296. #endif
  2297.  
  2298. #ifdef _WINDOWS
  2299.     pico_config_menu_items (keymenu);
  2300. #endif
  2301.  
  2302.     if(term.t_mrow == 0)
  2303.       return(FALSE);
  2304.  
  2305.     /*
  2306.      * Calculate amount of space for the names column by column...
  2307.      */
  2308.     for(index = 0; index < 6; index++)
  2309.       if(!(gmode&MDFKEY)){
  2310.       nspace[index] = (keymenu[index].name)
  2311.                 ? strlen(keymenu[index].name) : 0;
  2312.       if(keymenu[index+6].name 
  2313.          && (n = strlen(keymenu[index+6].name)) > nspace[index])
  2314.         nspace[index] = n;
  2315.  
  2316.       nspace[index]++;
  2317.       }
  2318.       else
  2319.     nspace[index] = (index < 4) ? 3 : 4;
  2320.  
  2321.     tspace = term.t_ncol/6;        /* total space for each item */
  2322.     index  = 0;
  2323.     for(row = 0; row <= 1; row++){
  2324.     linebuf[0] = '\0';
  2325.     obufp = &linebuf[0];
  2326.     for(slot = 0; slot < 6; slot++){
  2327.         if(keymenu[index].name && keymenu[index].label){
  2328.         if(gmode&MDFKEY){
  2329.             p = fkey;
  2330.             sprintf(fkey, "F%d", (2 * slot) + row + 1);
  2331.         }
  2332.         else
  2333.           p = keymenu[index].name;
  2334. #ifdef    MOUSE
  2335.         sprintf(nbuf, "%.*s %s", nspace[slot], p,
  2336.             keymenu[index].label);
  2337.         register_key(index,
  2338.                  (gmode&MDFKEY) ? F1 + (2 * slot) + row:
  2339.                  (keymenu[index].name[0] == '^')
  2340.                    ? (CTRL | keymenu[index].name[1])
  2341.                    : (keymenu[index].name[0] == 'S'
  2342.                   && !strcmp(keymenu[index].name, "Spc"))
  2343.                    ? ' '
  2344.                    : keymenu[index].name[0],
  2345.                  nbuf, invert_label,
  2346.                  term.t_nrow - 1 + row, (slot * tspace),
  2347.                  strlen(nbuf));
  2348. #endif
  2349.  
  2350.         n = nspace[slot];
  2351.         while(p && *p && n--){
  2352.             *obufp++ = '~';    /* insert "invert" token */
  2353.             *obufp++ = *p++;
  2354.         }
  2355.  
  2356.         while(n-- > 0)
  2357.           *obufp++ = ' ';
  2358.  
  2359.         p = keymenu[index].label;
  2360.         n = tspace - nspace[slot];
  2361.         while(p && *p && n-- > 0)
  2362.           *obufp++ = *p++;
  2363.  
  2364.         while(n-- > 0)
  2365.           *obufp++ = ' ';
  2366.         }
  2367.         else{
  2368.         n = tspace;
  2369.         while(n--)
  2370.           *obufp++ = ' ';
  2371.  
  2372. #ifdef    MOUSE
  2373.         register_key(index, NODATA, "", NULL, 0, 0, 0);
  2374. #endif
  2375.         }
  2376.  
  2377.         *obufp = '\0';
  2378.         index++;
  2379.     }
  2380.  
  2381.     wstripe(term.t_nrow - 1 + row, 0, linebuf, '~');
  2382.     }
  2383. }
  2384.